﻿
<Serializable()>
Public Class ObjectTypeLookup(Of T)
    Private mDefault As T
    Private mByType As Dictionary(Of ObjectType, T)
    Private mBySubType As Dictionary(Of ObjectType, Dictionary(Of ObjectSubType, T))

    Public Sub New()
        Clear()
    End Sub

    Public Sub RegisterValue(objectType As ObjectType, subType As ObjectSubType, value As T)
        If objectType = objectType.ALL AndAlso subType = ObjectSubType.ALL Then
            mDefault = value
        Else
            If objectType <> objectType.ALL AndAlso subType = ObjectSubType.ALL Then
                mByType(objectType) = value
            Else
                If objectType <> objectType.ALL AndAlso subType <> ObjectSubType.ALL Then
                    Dim typeLookup As Dictionary(Of ObjectSubType, T) = Nothing
                    If Not mBySubType.TryGetValue(objectType, typeLookup) Then
                        typeLookup = New Dictionary(Of ObjectSubType, T)
                        mBySubType.Add(objectType, typeLookup)
                    End If
                    typeLookup(subType) = value
                Else
                    'error subtype but not type
                End If
            End If
        End If
    End Sub

    Default Public ReadOnly Property Value(objectType As ObjectType, subType As ObjectSubType) As T
        Get
            'check type and subtype list
            If mBySubType.ContainsKey(objectType) AndAlso mBySubType(objectType).ContainsKey(subType) Then
                Return mBySubType(objectType)(subType)
            End If
            If EnableFallback Then
                'check just type list
                If mByType.ContainsKey(objectType) Then
                    Return mByType(objectType)
                End If
                'return the default object
                Return mDefault
            End If
        End Get
    End Property

    Public Function GetAllValues() As List(Of T)
        Dim ret As New List(Of T)
        If mDefault IsNot Nothing Then
            ret.Add(mDefault)
        End If
        ret.AddRange(mByType.Values)
        For Each subTypeLookup As Dictionary(Of ObjectSubType, T) In mBySubType.Values
            ret.AddRange(subTypeLookup.Values)
        Next
        Return ret
    End Function

    Public Sub RemoveValue(objectType As ObjectType, subType As ObjectSubType)
        If objectType = objectType.ALL AndAlso subType = ObjectSubType.ALL Then
            mDefault = Nothing
        Else
            If objectType <> objectType.ALL AndAlso subType = ObjectSubType.ALL Then
                mByType.Remove(objectType)
            Else
                If objectType <> objectType.ALL AndAlso subType <> ObjectSubType.ALL Then
                    Dim typeLookup As Dictionary(Of ObjectSubType, T) = Nothing
                    If Not mBySubType.TryGetValue(objectType, typeLookup) Then
                        typeLookup = New Dictionary(Of ObjectSubType, T)
                        mBySubType.Add(objectType, typeLookup)
                    End If
                    typeLookup.Remove(subType)
                Else
                    'error subtype but not type
                End If
            End If
        End If
    End Sub

    Public Sub Clear()
        mDefault = Nothing
        mByType = New Dictionary(Of ObjectType, T)
        mBySubType = New Dictionary(Of ObjectType, Dictionary(Of ObjectSubType, T))
    End Sub

    Public Property EnableFallback As Boolean = True
End Class
